#include<functional>
#include<thread>
#include<mutex>

#define LCCTOOLBOX_DLL_IMPORT_LCCTOOLBOX
#include<lcctoolbox_block.hpp>
#ifdef _WIN32
#pragma comment(lib,"lcctoolbox")
#endif

#include<glm/detail/type_vec1.hpp>
#include<glm/detail/type_vec2.hpp>
#include<glm/detail/type_vec3.hpp>
#include<glm/detail/type_vec4.hpp>
#include<glm/geometric.hpp>

namespace LccStuffCollector{
namespace ExtremeNhorz{



// the T_element should be glm::vec2 or glm::vec3

template<class T_element>
struct commonPart{
	std::function<size_t(void)> size;// get the points' count
	std::function<T_element&(size_t)> element;
	bool working() const {return size&&element;}
};

template<class T_point>
struct InputWrapper:public commonPart<T_point>{
	glm::vec3 cut_direction;
};

template<class T_point,class T_index>
struct OutputWrapper:public commonPart<T_index>{
	std::function<void(void)> clear;
	std::function<void(size_t)> grow;
	bool working() const {return commonPart<T_index>::working()&&clear&&grow;}
};


template<class T_point,class T_index=size_t>
void process( const InputWrapper<T_point> input, const OutputWrapper<T_point,T_index> output_extreme, const OutputWrapper<T_point,T_index> output_horz1, const OutputWrapper<T_point,T_index> output_horz, const OutputWrapper<T_point,T_index> output_horz2){
	if(!input.working()||!output_extreme.working()||!output_horz1.working()||!output_horz.working()||!output_horz2.working()) return;
	if(!input.size()) return;
	if(T_point::length()!=2||T_point::length()!=3) return;

	std::mutex extreme_mutex;
	std::mutex horz1_mutex;
	std::mutex horz_mutex;
	std::mutex horz2_mutex;
	output_extreme.clear();
	output_horz1.clear();
	output_horz.clear();
	output_horz2.clear();

	auto pieceExtremeNhorz=[&](size_t i,T_point& p1,T_point& p2,T_point& p3)
	{	glm::vec3 slot1;
		glm::vec3 slot2;
		auto temp1=p2-p1;
		auto temp2=p3-p2;
		for(int n=0;n<3;++n)
		{	slot1[n]=n<T_point::length()?temp1[n]:0;
			slot2[n]=n<T_point::length()?temp2[n]:0;
		}
		slot1=glm::cross(slot1,input.cut_direction);
		slot2=glm::cross(slot2,input.cut_direction);
		auto slot1_module=glm::length(slot1);
		auto slot2_module=glm::length(slot2);
		auto slot1_direction=glm::normalize(slot1);
		auto slot2_direction=glm::normalize(slot2);

		if( slot1_module!=0 && slot2_module!=0 )// "/\" or "//" or "\/" or "\\"
		{	if(slot1_direction!=slot2_direction)// "/\" or "\/"
			{	extreme_mutex.lock();
				output_extreme.grow(1);
				output_extreme.element(output_extreme.size()-1)=T_index(i);
				extreme_mutex.unlock();
			}
		}
		else if( slot1_module==0 && slot2_module==0 )// "--"
		{	horz_mutex.lock();
			output_horz.grow(1);
			output_horz.element(output_horz.size()-1)=T_index(i);
			horz_mutex.unlock();
		}
		else if(slot1_module==0)// "_*"
		{	horz1_mutex.lock();
			output_horz1.grow(1);
			output_horz1.element(output_horz1.size()-1)=T_index(i);
			horz1_mutex.unlock();
		}
		else// "*_"
		{	horz2_mutex.lock();
			output_horz2.grow(1);
			output_horz2.element(output_horz2.size()-1)=T_index(i);
			horz2_mutex.unlock();
		}
	};

	LccToolbox::block<std::thread> jobUnit{input.size()};

	for(size_t i=0;i<input.size();++i)
		jobUnit[i]=std::thread{pieceExtremeNhorz,i,i==0?input.element(input.size()-1):input.element(i-1),input.element(i),i==(input.size()-1)?input.element(0):input.element(i+1)};

	for(size_t i=0;i<jobUnit.size();++i) jobUnit[i].join();
}




}// namespace ExtremeNhorz
}// namespace LccStuffCollector